/**
 * Test Spec (using Jasmine Java Script Unit Test Framework)
 * http://pivotal.github.com/jasmine/
 * 
 * Contains unit tests that require access to a Paging Service
 */

beforeEach(function () {
	baseException = new VPAGER.Exception("","Any VPAGER Exception");
	validationEx = new VPAGER.Exception().ValidationError("");
	illegalParamsEx = new VPAGER.Exception().IllegalParameters("");
	srvRequestEx = new VPAGER.Exception().ServiceRequestError(
			"Service Request Error", "", "");
	
	// Customer Match for VPAGER.Exception objects
	this.addMatchers({
		toThrowVPagerEx:	function(expected) {
			  var result = false;
			  var exception = undefined;
			  if (typeof this.actual != 'function') {
			    throw new Error('Actual is not a function');
			  }
			  try {
			    this.actual();
			  } catch (e) {
			    exception = e;
			  }
			  if (exception) {
			    result = (exception instanceof VPAGER.Exception &&
			    		  expected instanceof VPAGER.Exception &&
			    		  (expected.name === "" || exception.name === expected.name));
			  }
			
			  var not = this.isNot ? "not " : "";
			
			  this.message = function() {
			    if (exception) {
			      return ["Expected function " + not + "to throw", expected ? expected.message || expected : "an exception", ", but it threw", exception.message || exception].join(' ');
			    } else {
			      return "Expected function to throw an exception.";
			    }
			  };
			
			  return result;
		}
	});
});

describe("Logging", function() {

	describe("Log4Javascript Setup", function() {
		it("Should accept log4javascript", function() {
            if (!VPAGER.Logging().getLogger() ||
                VPAGER.Logging().getLogger().ID !== "L4JS")
            {
                // Create an L4JS logger object to redirect log message to TRACE.
                var logger = VPC.createProductionLogger();
            
                // Install Log4Javascript so trace logging is now in place for 
                // remainder of unit tests.
                new VPAGER.Logging().setLogger(logger);
                expect(VPAGER.Logging().getLogger()).toEqual(logger);
            }
		});
	});
});


describe("ServerProxy API Tests", function() {
	var sp1 = null;
	var sp2 = null;
	var pcode1 = 'T01';
	var pcode2 = 'T02';
	var page1 = null;
	var pageCount = null;
	var request1 = null;
	var request2 = null;

	beforeEach(function() {
		sp1 = new VPAGER.ServerProxy();
		request1 = new VPAGER.ParentPage({ Code: pcode1 });
		request2 = new VPAGER.ParentPage({ Code: pcode2 });
	});

	it("Should initialize", function() {
        sp1.initialize( { serviceURI: SERVICE_URI } );
		expect(sp1.getServiceURI()).toEqual(SERVICE_URI);
	});

	it("Should be a Singleton", function() {
		sp2 = new VPAGER.ServerProxy();
		expect(sp1).toEqual(sp2);
		expect(sp2.getServiceURI()).toEqual(SERVICE_URI);
		expect(new VPAGER.ServerProxy().getServiceURI()).toEqual(SERVICE_URI);
	});

	it("Can get Service Info", function() {
		var info = sp1.getServiceInfo();
		expect(info).toBeDefined();
		expect(info.getStatus().getOverallStatus()).toBeDefined();
		expect(info.getUpTime()).toBeGreaterThan(-1);
	});
	
	it("Can get Page List", function() {
		expect(function(){
		          pageCount = sp1.getPages().length;
		}).not.toThrowVPagerEx(baseException);
		expect(pageCount).toBeGreaterThan(-1);
	});
	

	/**
	 * ========================
	 * Page Web API Test Suites
	 * ========================
	 */
	
	describe("Page Creation API Tests", function() {
		beforeEach(function() {
			//Clear current pages
			sp1.cancelAllPages();
			initPageCount = sp1.getPages().length;
		});

		it("Can create a Parent Page", function() {
			expect(function(){
				 		page1 = sp1.submitNewPage(request1);
			   	   }).not.toThrowVPagerEx(baseException);
			
			expect(sp1.getPages().length).toEqual(initPageCount + 1);
		});
		
        it("Can expect sane Create Times", function() {
            // Verify basic creation time availability
            var startTime1 = (new Date()).getTime();
            page1 = sp1.submitNewPage(request1);
            expect(page1.getCreationTime() instanceof Date).toBeTruthy();
            var p1Time = page1.getCreationTime().getTime();
            
            // Calculate a time offset between client and server
            var offset1 = Math.abs(p1Time - startTime1);
            
            // Verify second page request has a later time stamp than first
            var startTime2 = (new Date()).getTime();
            page2 = sp1.submitNewPage(request2);
            var p2Time = page2.getCreationTime().getTime();
            expect(p2Time).toBeGreaterThan(p1Time);
            
            var offset2 = Math.abs(p2Time - startTime2);
            // Check times are within +/- 50% of original offset
            expect(offset2).toBeGreaterThan(offset1 * -1.5);
            expect(offset2).toBeLessThan(offset1 * 1.5);
            
        });
		
		it("Can handle null data", function() {
			expect(function(){
		 		page1 = sp1.submitNewPage(null);
			}).toThrowVPagerEx(illegalParamsEx);
		});
		
		describe("Parent Page Creation API Tests", function() {
			beforeEach(function() {
				//Clear current pages
				sp1.cancelAllPages();
				initPageCount = sp1.getPages().length;
			});

			it("Can handle empty data", function() {
				var exception = VPC.testParentPageAdd({});
				expect(exception).not.toBeNull();
				expect(exception.errorCode).toEqual(10409);
			});
			
			it("Can handle bad data - Empty code", function() {
				VPC.testBadParentPageCode("");  // Empty
			});
			
			it("Can handle bad data - Code too long", function() {
				VPC.testBadParentPageCode("1234"); // Too Long
			});
			
			it("Can handle bad data - Code invalid characters", function() {
				VPC.testBadParentPageCode("C@T"); // Non-alphanumeric
			});
		});
		
		it("Server should not allow duplicate Pages", function() {
			page1 = sp1.submitNewPage(request2);
			expect(page1.getCode()).toEqual(pcode2);
			expect(sp1.getPages().length).toEqual(initPageCount + 1);
			
			// Resubmit Page and check exception thrown
			expect(function(){
				sp1.submitNewPage(request2);;
	   	    }).toThrowVPagerEx(srvRequestEx);
		});
		
		describe("Parent Page Modification API Tests", function() {
			beforeEach(function() {
				//Clear current pages
				sp1.cancelAllPages();
				initPageCount = sp1.getPages().length;
			});

			it("Can Modify A Parent Page", function() {
				var code = "X11";
				var location = "Testing";
				var location2 = "Testing Still";
				var isUrgent2 = true;
				var tag2 = "vPager Forever";
				
				var request = new VPAGER.ParentPage(
						{ Code: code,
						  Location: location });
				
				// Create initial page item
				page1 = sp1.submitNewPage(request);
				expect(page1.getCode()).toEqual(code);
				expect(page1.getLocation()).toEqual(location);
				
				// Modify a single property
				page1.setLocation(location2);
				page2 = sp1.modifyPage(page1);
				expect(page2.getCode()).toEqual(code);
				expect(page2.getID()).toEqual(page1.getID());
				expect(page2.getLocation()).toEqual(location2);
				
				// Modify multiple properties
				page1.setIsUrgent(isUrgent2);
				page1.setTag(tag2);
				page2 = sp1.modifyPage(page1);
				expect(page2.getCode()).toEqual(code);
				expect(page2.getID()).toEqual(page1.getID());
				expect(page2.getLocation()).toEqual(location2);
				expect(page2.getTag()).toEqual(tag2);
				expect(page2.getIsUrgent()).toEqual(isUrgent2);
				
			});
			
		});
		
	});	

	describe("Page Cancel API Tests", function() {
		beforeEach(function() {
			request1 = new VPAGER.ParentPage({ Code: pcode1 });
			request2 = new VPAGER.ParentPage({ Code: pcode2 });
			//Clear current pages
			sp1.cancelAllPages();
			initPageCount = sp1.getPages().length;
		});

		it("Can cancel a Page", function() {
			var rv = undefined;
			page1 = sp1.submitNewPage(request1);
			expect(function(){
				 		rv = sp1.cancelPage(page1);
			   	   }).not.toThrowVPagerEx(baseException);
			expect(rv).toBeTruthy();
			expect(sp1.getPages().length).toEqual(initPageCount);
		});
	
		it("Can cancel all Pages", function() {
			var rv = undefined;
			var pageCount = undefined;
			
			page1 = sp1.submitNewPage(request1);
			page2 = sp1.submitNewPage(request2);
			pageCount = sp1.getPages().length;
			expect(pageCount).toEqual(initPageCount + 2);
			
			expect(function(){
				 		rv = sp1.cancelAllPages();
			   	   }).not.toThrowVPagerEx(baseException);
			expect(rv).toEqual(pageCount);
			expect(sp1.getPages().length).toEqual(0);
		});
	});

    describe("Core Display Handler API Tests", function() {
        beforeEach(function() {
            request1 = new VPAGER.ParentPage({ Code: pcode1 });
            request2 = new VPAGER.ParentPage({ Code: pcode2 });
            //Clear current pages
            sp1.cancelAllPages();
            initPageCount = sp1.getPages().length;
        });

        it("Can Enable/Disable Pager Display", function() {
            var params = new VPAGER.DisplayParams();
            params.setViewState("ENABLED");
            
            expect(function(){
                sp1.setDisplayParams(params);
            }).not.toThrowVPagerEx(baseException);
            
            expect(sp1.getServiceInfo().getStatus().getDisplayState()).toEqual("ENABLED");
            
            params.setViewState("DISABLED");
            sp1.setDisplayParams(params);
            
            expect(sp1.getServiceInfo().getStatus().getDisplayState()).toEqual("DISABLED");
            
        });

        it("Can Handle Bad Parameters", function() {
            var params = new VPAGER.DisplayParams();
            params.setViewState("BAD_VALUE");
            
            expect(function(){
                sp1.setDisplayParams(params);
            }).toThrowVPagerEx(baseException);          
        });
        
        it("Can Handle Forced Disabling", function() {
            // Add some Pages
            page1 = sp1.submitNewPage(request1);
            page2 = sp1.submitNewPage(request2);

            // Make sure Display is Enabled
            VPC.testDisplayViewState("ENABLED");
            expect(sp1.getServiceInfo().getStatus().getDisplayState()).toEqual("ENABLED");
            
            // Now attempt to DISABLE with Active Pages
            var exception = VPC.testDisplayViewState("DISABLED");
            expect(exception).not.toBeNull();
            expect(exception.errorCode).toEqual(50409);

            // Display should still be Enabled
            expect(sp1.getServiceInfo().getStatus().getDisplayState()).toEqual("ENABLED");

            // Now attempt to FORCE DISABLE with Active Pages
            var params = new VPAGER.DisplayParams();
            params.setViewState("DISABLED");
            params.setForceDisable(true);
            var exception = VPC.captureException(
                    new VPAGER.ServerProxy().setDisplayParams, params);
            expect(exception).toBeNull();

            // Display should still be Enabled 
            expect(sp1.getServiceInfo().getStatus().getDisplayState()).toEqual("DISABLED");
            // The 2 pages created for this test should still be present
            expect(sp1.getPages().length).toEqual(initPageCount + 2);
        });
        
        it("Can Retrieve Current Displayed Page", function() {
            // Add some Pages
            page1 = sp1.submitNewPage(request1);
            page2 = sp1.submitNewPage(request2);
            var cp;
            var np;

            // Make sure Display is Enabled
            VPC.testDisplayViewState("ENABLED");
            expect(sp1.getServiceInfo().getStatus().getDisplayState()).toEqual("ENABLED");
            
            // Wait 2 seconds for page displays to start
            waits(2000);
            
            runs(function() {
                cp = sp1.getDisplayCurrentPage();
                if (cp.sameAs(page1)) {
                    np = page2;
                } else {
                    np = page1;
                }
            });
            
            // Wait 4.5 seconds for next Page to be displayed
            // Page display time is 3 seconds plus 1 second of transition.
            waits(4500);
            
            runs(function() {
                cp = sp1.getDisplayCurrentPage();
                expect(cp.sameAs(np)).toBeTruthy();
            });
        });
    });

    describe("DisplayHandler Tests", function() {
        beforeEach(function() {
            request1 = new VPAGER.ParentPage({ Code: pcode1 });
            request2 = new VPAGER.ParentPage({ Code: pcode2 });
            //Clear current pages
            sp1.cancelAllPages();
            display = sp1.getDisplayHandler();
            // Start each test with an Enabled Display
            var params = new VPAGER.DisplayParams();
            params.setViewState("ENABLED");
            expect(display.isDisplayEnabled()).toBeTruthy();
        });

        it("Can Enable/Disable Pager Display", function() {
            // Disable Display
            display.disableDisplay();
            expect(display.isDisplayEnabled()).toBeFalsy();
            expect(sp1.getServiceInfo().getStatus().getDisplayState()).toEqual("DISABLED");
            
            // Enable Display
            display.enableDisplay();
            expect(display.isDisplayEnabled()).toBeTruthy();
            expect(sp1.getServiceInfo().getStatus().getDisplayState()).toEqual("ENABLED");
        });

        it("Can Handle Force Disabling of Pager Display", function() {
            // Add some Pages
            page1 = sp1.submitNewPage(request1);
            page2 = sp1.submitNewPage(request2);

            // Try standard unforced disable should fail           
            var exception = VPC.captureException(display.disableDisplay);
            expect(exception).not.toBeNull();
            expect(exception.errorCode).toEqual(50409);
            expect(display.isDisplayEnabled()).toBeTruthy();
            expect(sp1.getServiceInfo().getStatus().getDisplayState()).toEqual("ENABLED");
           
            // Try forcing disable should succeed           
            var exception = VPC.captureException(display.disableDisplay, true);
            expect(exception).toBeNull();
            expect(display.isDisplayEnabled()).toBeFalsy();
           
        });

	});

    describe("Page Monitor Tests", function() {
        beforeEach(function() {
            // Create to Listeners for testing
            listener1 = new VPC.SimpleListener();
            listener2 = new VPC.SimpleListener();
       });

        it("Can Enable PageMonitor", function() {
            var pagemon = sp1.getPageMonitor();
            expect(pagemon.isEnabled()).toBeFalsy();
            expect(pagemon.isRunning()).toBeFalsy();
            
            pagemon.enable();
            expect(pagemon.isEnabled()).toBeTruthy();
            expect(pagemon.isRunning()).toBeFalsy();
            
            pagemon.registerChangeListener(listener1);
            expect(pagemon.isRunning()).toBeTruthy();
           
            pagemon.disable();
            expect(pagemon.isRunning()).toBeFalsy();

            pagemon.enable();
            expect(pagemon.isRunning()).toBeTruthy();

            pagemon.deregisterChangeListener(listener1);
            expect(pagemon.isRunning()).toBeFalsy();
        });

        it("Can receive Add Page events", function() {
            var pagemon = sp1.getPageMonitor();
            pagemon.setRefreshRate(1);
            expect(pagemon.getRefreshRate()).toEqual(1);
            
            //Clear current pages
            sp1.cancelAllPages();
            // Force local pagemon object to synch with server
            pagemon.refresh();

            pagemon.registerChangeListener(listener1);
            pagemon.enable();
            expect(pagemon.isRunning()).toBeTruthy();
            
            waits(1500);
            runs(function() {
                // 0 Add = 0 Events
                expect(listener1._called).toEqual(0);
                page1 = sp1.submitNewPage(request1);
            });
            
            waits(1500);
            runs(function() {
                // 1 Add = 1 Events
                expect(listener1._called).toEqual(1);
                var pages = listener1._event;
                expect(pages.length).toEqual(1);
                expect(pages[0].exactSameAs(page1)).toBeTruthy();
                // Resubmit same page - catch/ignore exception
                try {
                    sp1.submitNewPage(request1);
                } catch (e){}
            });

            waits(1500);
            runs(function() {
                // Last add should have failed (duplicate)
                // 1 Add = 1 Events
                expect(listener1._called).toEqual(1);
            });

          });
          
        it("Can receive Modified Page events", function() {
            var pagemon = sp1.getPageMonitor();
            var page1 = undefined;
            pagemon.disable();
            pagemon.setRefreshRate(1);
            expect(pagemon.getRefreshRate()).toEqual(1);
            
            //Clear current pages at server
            sp1.cancelAllPages();
            // Force local pagemon object to synch with server
            pagemon.refresh();
            
            pagemon.registerChangeListener(listener1);
            pagemon.enable();
            expect(pagemon.isRunning()).toBeTruthy();
            
            waits(1500);
            runs(function() {
                // Add a page
                expect(listener1._called).toEqual(0);
                page1 = sp1.submitNewPage(request1);
            });
            
            waits(1500);
            runs(function() {
                expect(listener1._called).toEqual(1);
                // Modify page 
                page1.setLocation("Testville");
                sp1.modifyPage(page1);
            });

            waits(1500);
            runs(function() {
                // 1 Add + 1 Modify = 2 Events
                expect(listener1._called).toEqual(2);
                // Re-Modify page (No Actual Changes) 
                sp1.modifyPage(page1);
            });

            waits(1500);
            runs(function() {
                // Last Modify call did not change anything so
                // we do not expect another event to listener
                expect(listener1._called).toEqual(2);
            });

          });
         
        it("Can Disable PageMonitor", function() {
            var pagemon = sp1.getPageMonitor();
            // Call after test to disable polling loop.
            pagemon.disable();
         });
    });

    describe("Status Monitor Tests", function() {
        beforeEach(function() {
            // Create to Listeners for testing
            listener1 = new VPC.SimpleListener();
            listener2 = new VPC.SimpleListener();
       });

        it("Can Enable StatusMonitor", function() {
            var statusmon = sp1.getStatusMonitor();
            expect(statusmon.isEnabled()).toBeFalsy();
            expect(statusmon.isRunning()).toBeFalsy();
            
            statusmon.enable();
            expect(statusmon.isEnabled()).toBeTruthy();
            expect(statusmon.isRunning()).toBeFalsy();
            
            statusmon.registerChangeListener(listener1);
            expect(statusmon.isRunning()).toBeTruthy();
           
            statusmon.disable();
            expect(statusmon.isRunning()).toBeFalsy();

            statusmon.enable();
            expect(statusmon.isRunning()).toBeTruthy();

            statusmon.deregisterChangeListener(listener1);
            expect(statusmon.isRunning()).toBeFalsy();
        });

        it("Can Receive Display Events", function() {
            var statusmon = sp1.getStatusMonitor();
            statusmon.setRefreshRate(1);
            expect(statusmon.getRefreshRate()).toEqual(1);
            
            //Make sure Display is off (force)
            sp1.getDisplayHandler().disableDisplay(true);
            // Force local statusmon object to synch with server
            statusmon.refresh();
 
            statusmon.registerChangeListener(listener1);
            statusmon.enable();
            expect(statusmon.isRunning()).toBeTruthy();
            
            waits(1500);
            runs(function() {
                // No changes so far
                expect(listener1._called).toEqual(0);
                // Turn Display On
                sp1.getDisplayHandler().enableDisplay();
            });
            
            waits(1500);
            runs(function() {
                // Display Status Changed
                expect(listener1._called).toEqual(1);
                var status = listener1._event;
                expect(status.getDisplayState()).toEqual("ENABLED");
                // Verify the status returned via event matches the
                // one returned by the StatusMonitor directly.
                expect(statusmon.getStatus().sameAs(status)).toBeTruthy();
                 // Turn Display On (no effect)
                sp1.getDisplayHandler().enableDisplay();
           });

            waits(1500);
            runs(function() {
                // Display Status Should Not Be Changed
                expect(listener1._called).toEqual(1);
                var status = listener1._event;
                expect(status.getDisplayState()).toEqual("ENABLED");
                //Make sure Display is off (force)
                sp1.getDisplayHandler().disableDisplay(true);
           });

            waits(1500);
            runs(function() {
                // Display Status Should Be Changed
                expect(listener1._called).toEqual(2);
                var status = listener1._event;
                expect(status.getDisplayState()).toEqual("DISABLED");
           });

          });
          
        it("Can Disable StatusMonitor", function() {
            var statusmon = sp1.getStatusMonitor();
            // Call after test to disable polling loop.
            statusmon.disable();
        });
    });

});



